using System;

namespace Hsenl {
    /// <summary>
    /// 加密帮助者。
    /// </summary>
    public static class EncryptionHelper {
        internal const int QuickEncryptLength = 220;

        /// <summary>
        /// 将 bytes 使用 code 做异或运算的快速版本。
        /// </summary>
        /// <param name="bytes">原始二进制流。</param>
        /// <param name="code">异或二进制流。</param>
        /// <returns>异或后的二进制流。</returns>
        public static byte[] GetQuickXorBytes(byte[] bytes, byte[] code) {
            return GetXorBytes(bytes, 0, QuickEncryptLength, code);
        }

        /// <summary>
        /// 将 bytes 使用 code 做异或运算的快速版本。此方法将复用并改写传入的 bytes 作为返回值，而不额外分配内存空间。
        /// </summary>
        /// <param name="bytes">原始及异或后的二进制流。</param>
        /// <param name="code">异或二进制流。</param>
        public static void GetQuickSelfXorBytes(byte[] bytes, byte[] code) {
            GetSelfXorBytes(bytes, 0, QuickEncryptLength, code);
        }

        /// <summary>
        /// 将 bytes 使用 code 做异或运算。
        /// </summary>
        /// <param name="bytes">原始二进制流。</param>
        /// <param name="code">异或二进制流。</param>
        /// <returns>异或后的二进制流。</returns>
        public static byte[] GetXorBytes(byte[] bytes, byte[] code) {
            if (bytes == null) {
                return null;
            }

            return GetXorBytes(bytes, 0, bytes.Length, code);
        }

        /// <summary>
        /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值，而不额外分配内存空间。
        /// </summary>
        /// <param name="bytes">原始及异或后的二进制流。</param>
        /// <param name="code">异或二进制流。</param>
        public static void GetSelfXorBytes(byte[] bytes, byte[] code) {
            if (bytes == null) {
                return;
            }

            GetSelfXorBytes(bytes, 0, bytes.Length, code);
        }

        /// <summary>
        /// 将 bytes 使用 code 做异或运算。
        /// </summary>
        /// <param name="bytes">原始二进制流。</param>
        /// <param name="startIndex">异或计算的开始位置。</param>
        /// <param name="length">异或计算长度，若小于 0，则计算整个二进制流。</param>
        /// <param name="code">异或二进制流。</param>
        /// <returns>异或后的二进制流。</returns>
        public static byte[] GetXorBytes(byte[] bytes, int startIndex, int length, byte[] code) {
            if (bytes == null) {
                return null;
            }

            var bytesLength = bytes.Length;
            var results = new byte[bytesLength];
            System.Array.Copy(bytes, 0, results, 0, bytesLength);
            GetSelfXorBytes(results, startIndex, length, code);
            return results;
        }

        /// <summary>
        /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值，而不额外分配内存空间。
        /// </summary>
        /// <param name="bytes">原始及异或后的二进制流。</param>
        /// <param name="startIndex">异或计算的开始位置。</param>
        /// <param name="length">异或计算长度。</param>
        /// <param name="code">异或二进制流。</param>
        public static void GetSelfXorBytes(byte[] bytes, int startIndex, int length, byte[] code) {
            if (bytes == null) {
                return;
            }

            if (code == null) {
                throw new Exception("Code is invalid.");
            }

            var codeLength = code.Length;
            if (codeLength <= 0) {
                throw new Exception("Code length is invalid.");
            }

            if (startIndex < 0 || length < 0 || startIndex + length > bytes.Length) {
                throw new Exception("Start index or length is invalid.");
            }

            var codeIndex = startIndex % codeLength;
            for (var i = startIndex; i < length; i++) {
                bytes[i] ^= code[codeIndex++];
                codeIndex %= codeLength;
            }
        }

        public static void GetQuickXorBytes(Span<byte> span, Span<byte> code) {
            if (span == null) {
                return;
            }

            if (code == null) {
                throw new Exception("Code is invalid.");
            }

            var codeLength = code.Length;
            if (codeLength <= 0) {
                throw new Exception("Code length is invalid.");
            }

            var codeIndex = 0;
            var len = span.Length;
            for (var i = 0; i < len; i++) {
                span[i] ^= code[codeIndex++];
                codeIndex %= codeLength;
            }
        }
    }
}